﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FileStorage.Caching.CacheContainers
{
    /// <summary>
    /// Base class that could (not required) be used to derive cache containers functionality.
    /// </summary>
    public abstract class BaseCacheContainer : ICacheContainer
    {
        /// <summary>
        /// This dictionary holds the in mem objects
        /// </summary>
        protected Dictionary<string, object> cachedItems = new Dictionary<string, object>();
        protected Dictionary<string, int> cachedItemsUsageFrequency = new Dictionary<string, int>();
        protected Dictionary<string, DateTime> cachedItemsLastAccessUTC = new Dictionary<string, DateTime>();
        protected Dictionary<string, DateTime> cachedItemsRetrievedDatetimeUTC = new Dictionary<string, DateTime>();

        public BaseCacheContainer()
        {
            //
            // registers the cache container to the facade such that this container will be cleared when the invalidateAll method is invoked on
            // the cacher
            //
            FileStorageCacherFacade.RegisterCacheContainer(this);
        }

        public abstract bool RequiresCleaning(string key);

        #region ICacheContainer Members

        public void Validate(string fileStorageName, Guid dataIdentifier, FileStorageCacherFacade.CacheMethodInvocationsEnum cacheMethodInvocationsEnum)
        {
            string key = MakeCacheKey(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);

            if (RequiresCleaning(key))
            {
                cachedItems.Remove(key);
                cachedItemsUsageFrequency.Remove(key);
                cachedItemsLastAccessUTC.Remove(key);
                cachedItemsRetrievedDatetimeUTC.Remove(key);
            }

            //
            // update frequency table
            //
            if (cachedItemsUsageFrequency.ContainsKey(key))
            {
                if (cachedItemsUsageFrequency[key] < int.MaxValue)
                {
                    cachedItemsUsageFrequency[key]++;
                }
            } 
            else
            {
                cachedItemsUsageFrequency[key] = 1;
            }

            cachedItemsLastAccessUTC[key] = DateTime.UtcNow;
        }

        public bool Exists(string fileStorageName, Guid dataIdentifier, FileStorageCacherFacade.CacheMethodInvocationsEnum cacheMethodInvocationsEnum)
        {
            string key = MakeCacheKey(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);
            return cachedItems.ContainsKey(key);
        }

        public object Get(string fileStorageName, Guid dataIdentifier, FileStorageCacherFacade.CacheMethodInvocationsEnum cacheMethodInvocationsEnum)
        {
            string key = MakeCacheKey(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);
            return cachedItems[key];
        }

        public void Store(string fileStorageName, Guid dataIdentifier, FileStorageCacherFacade.CacheMethodInvocationsEnum cacheMethodInvocationsEnum, object objectToStore)
        {
            string key = MakeCacheKey(fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);
            cachedItemsRetrievedDatetimeUTC[key] = DateTime.UtcNow;
            cachedItems[key] = objectToStore;
        }

        public void InvalidateAll()
        {
            cachedItems.Clear();
            cachedItemsUsageFrequency.Clear();
            cachedItemsLastAccessUTC.Clear();
            cachedItemsRetrievedDatetimeUTC.Clear();
        }

        public int Count()
        {
            return cachedItems.Count;
        }

        #endregion

        public string MakeCacheKey(string fileStorageName, Guid dataIdentifier, FileStorageCacherFacade.CacheMethodInvocationsEnum cacheMethodInvocationsEnum)
        {
            return string.Format("{0}_{1}_{2}", fileStorageName, dataIdentifier, cacheMethodInvocationsEnum);
        }
    }
}
